﻿using System;

namespace AdaptiveAgents.Distributions
{
    /// <summary>
    /// Represents a normal (gaussian) distribution
    /// </summary>
    public class Gaussian :  IDistribution
    {
        //Data members
        private CenterSpace.Free.NormalDist _dist;
        private double _F0 = 0;
        private double _F1 = 0;
        private double _intervalOfNormalization = 1;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="mean">μ - mean of the distribution</param>
        /// <param name="variance">σ - variance of the distribution</param>
        public Gaussian(double mean, double variance)
        {
            if (variance <= 0)
                throw new System.ArgumentException("variance should be greater than zero", "variance");
            
            _dist = new CenterSpace.Free.NormalDist(mean, variance);
            
            normalize();
        }

        #region private methods

        /// <summary>
        /// Normalization of the distriburion
        /// </summary>
        private void normalize()
        {
            _F0 = _dist.CDF(0);
            _F1 = _dist.CDF(1);
            _intervalOfNormalization = _F1 - _F0;
        }

        /// <summary>
        /// Generates random values of mean and variance according the the given interval
        /// </summary>
        /// <param name="start">start point of the interval</param>
        /// <param name="end">end point of the interval</param>
        private void generateRandomValues(double start, double end)
        {
            //Calcultate interval length
            double interval = end - start;

            //Calculate mean and variance
            _dist.Mean = interval * Generator.getNextDouble();
            _dist.Variance = interval * Generator.getNextDouble();
        }

        #endregion

        #region IDistribution Members

        /// <summary>
        /// Gets μ (mean of the gaussian distribution)
        /// </summary>
        public double Mean
        {
            get { return _dist.Mean; }
        }

        /// <summary>
        /// Gets σ² (variance of the gaussian distribution)
        /// </summary>
        public double Variance
        {
            get { return _dist.Variance; }
        }

        /// <summary>
        /// Generates ranodm mean and variance from the interval [0,10]
        /// </summary>
        public void generateRandomValues()
        {
            generateRandomValues(0, 10);
            normalize();
        }

        /// <summary>
        /// Gets f(x) (probability density function)
        /// </summary>
        /// <param name="x">Random variable</param>
        /// <returns>f(x) according to the distribution function</returns>
        public double valueOf(double x)
        {
            //x /= _intervalOfNormalization;
            return _dist.PDF(x) / _intervalOfNormalization;
        }

        /// <summary>
        /// Gets F(x) (probability cumulative density function)
        /// </summary>
        /// <param name="x">Random variable</param>
        /// <returns>F(x) according to the gaussian distribution function</returns>
        public double cumulativeValueOf(double x)
        {
            //x /= _intervalOfNormalization;

            return _dist.CDF(x) / _intervalOfNormalization;
        }

        /// <summary>
        /// Gets a sample of the distribution values
        /// </summary>
        /// <returns>Array of points that were sampled from the distribution function</returns>
        public Point[] sample()
        {
            int size = 100;
            double step = 0.01;
            Point[] sample = new Point[size];
            double limit = size * step;
            int i = 0;

            for (double x = 0.0; x < limit; x += step)
                sample[i++] = new Point(x, valueOf(x)); 

            return sample;
        }

        /// <summary>
        /// Gets the distribution type
        /// </summary>
        public DistributionType Type
        {
            get { return DistributionType.Gaussian; }
        }

        /// <summary>
        /// Gets a description of the distribution
        /// </summary>
        /// <returns>String representation of the gaussian distribution</returns>
        public String ToString()
        {
            return String.Format("{0}: {1},{2}", Type.ToString(), _dist.Mean, _dist.Variance);
        }

        /// <summary>
        /// Generates random value
        /// </summary>
        /// <returns>Choose random x and returns f(x)</returns>
        public double generateValue()
        {
            return _dist.PDF(Generator.getNextDouble());
        }

        #endregion
    }
}
